#include "BinanceData.h"

BinanceData* BinanceData::dataHandler = nullptr;

BinanceData* BinanceData::getInstance() {
    if (dataHandler == nullptr) {
        dataHandler = new BinanceData();
    }
    return dataHandler;
}

void BinanceData::addCoinTick(std::string inst, double ask, double bid) {
    auto it = coinMp.find(inst);
    if (it != coinMp.end()) {
        coinMp[inst].ask1 = ask;
        coinMp[inst].bid1 = bid;
    } else {
        coinMp[inst] = {inst, ask, bid};
    }
    //std::cout << inst << " " << coinMp[inst].ask1 << " " << coinMp[inst].bid1 << std::endl;
}

double BinanceData::getPrice(const std::string& inst, const char& type) {
    auto it = coinMp.find(inst);
    if (it != coinMp.end()) {
        return type == 'a' ? coinMp[inst].ask1 : coinMp[inst].bid1;
    }
    return -1;
}

std::vector<std::string> BinanceData::getSymbols() {
    std::vector<std::string> res;
    for (const auto& pair : coinMp) {
        res.push_back(pair.first);
    }
    return res;
}

void BinanceData::connect() {
    while (true) {
        websocketpp::lib::error_code ec;
        client::connection_ptr con = m_data_client.get_connection(dataUrl, ec);
        if (ec) {
            std::cout << "Could not create data connection because: " << ec.message() << std::endl;
            return;
        }
        m_data_client.connect(con);
        std::thread data_ws(&BinanceData::run_data_client, this);
        data_ws.join();
        m_data_client.reset();
    }
}

void BinanceData::run_data_client() {
    m_data_client.run();
}

void BinanceData::on_data_message(websocketpp::connection_hdl hdl, message_ptr msg) {
    nlohmann::json json_data = nlohmann::json::parse(std::string(msg->get_payload()));
    //std::cout << json_data << std::endl;
    for (auto coin_json : json_data) {
        std::string symbol = coin_json["s"];
        double ask = std::stod(std::string(coin_json["a"]).c_str());
        double bid = std::stod(std::string(coin_json["b"]).c_str());
        addCoinTick(symbol, ask, bid);
    }
}

void BinanceData::on_data_open(websocketpp::connection_hdl hdl) {
    std::cout << "Connected to Market Data WebSocket" << std::endl;
}

void BinanceData::on_data_close(websocketpp::connection_hdl hdl) {
    std::cout << "Disconnected from Market Data WebSocket" << std::endl;
}

BinanceData::BinanceData() {
    m_data_client.clear_access_channels(websocketpp::log::alevel::all);
    m_data_client.clear_error_channels(websocketpp::log::elevel::all);
    m_data_client.init_asio();
    m_data_client.set_tls_init_handler([](websocketpp::connection_hdl) {
        return websocketpp::lib::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::tlsv12);
    });
    m_data_client.set_message_handler(std::bind(&BinanceData::on_data_message, this, std::placeholders::_1, std::placeholders::_2));
    m_data_client.set_open_handler(std::bind(&BinanceData::on_data_open, this, std::placeholders::_1));
    m_data_client.set_close_handler(std::bind(&BinanceData::on_data_close, this, std::placeholders::_1));
}
